/*

 Copyright 2012 Peter Dornbach.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
   
*/

package com.dornbachs.zebra.guitarear;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.Vector;

import android.content.Context;
import android.util.Log;

public class UserState {
	public UserState() {
		resetScore();
	}
	
	public void resetScore() {
		_achievedLevels = new Vector<Level>();
		_achievedLevels.add(new Level());
	}
	
	public int score() {
		// The score is basically the number of available states with
		// the given set of achieved levels.
		//
		// See http://www.proofwiki.org/wiki/Cardinality_of_Set_Union
		// for how this works.
		//
		// Cheat: for a perfect score we should subtract the number of
		// states that are available but invalid. But this is complex
		// enough and no one cares anyway :)
		
		int score = 0;
		// With the current settings we should never have more 12.
		// If we have more, then we crop to 12 and produce an incorrect
		// score (instead of exponentially growing runtime).
		int l = Math.min(_achievedLevels.size(), 12);
		Vector<Level> levels = new Vector<Level>(l);
		for (int i = 1; i < 1 << l; ++i) {
			levels.clear();
			for (int j = 0; j < l; ++j) {
				if ((i & 1 << j) != 0) {
					levels.add(_achievedLevels.get(j));
				}
			}
			int sign = ((levels.size() & 1) == 0 ? -1 : 1);
			int partialScore = Level.intersect(levels).score();
			score += sign * partialScore;
		}
		return score - 1;
	}
	
	public void addLevel(Level level) {
		boolean hasRemoved = false;
		for (Iterator<Level> it = _achievedLevels.iterator(); it.hasNext();) {
			Level l = it.next();
			if (level.lessOrEqual(l)) {
				assert !hasRemoved;
				return;
			}
			if (l.lessOrEqual(level)) {
				hasRemoved = true;
				it.remove();
			}
		}
		_achievedLevels.add(level);
	}

	public boolean needsImprove(Level currentLevel, int dimension) {
		Level targetLevel = currentLevel.clone();
		targetLevel.set(dimension, targetLevel.get(dimension) + 1);
		for (Level l: _achievedLevels) {
			if (targetLevel.lessOrEqual(l)) {
				return false;
			}
		}
		return true;
	}

	public void trimLevel(Level level, int stableIdx) {
		int maxValue = -1;
		Level maxLevel = null;
		for (Level l: _achievedLevels) {
			int value = l.get(stableIdx);
			if (value > maxValue) {
				maxValue = value;
				maxLevel = l;
			}
		}
		level.trim(maxLevel);
	}

	public Level defaultBaseLevel() {
		if (_achievedLevels.size() > 0) {
			Level result = _achievedLevels.get(0).clone();
			for (int i = 1; i < _achievedLevels.size(); ++i) {
				result.trim(_achievedLevels.get(i));
			}
			return result;
		} else {
			return new Level();
		}
	}

	public String toString() {
		StringBuffer result = new StringBuffer();
		for (int i = 0; i < _achievedLevels.size(); ++i) {
			if (i > 0) {
				result.append(';');
			}
			result.append(_achievedLevels.get(i).toString());
		}
		return result.toString();
	}

	public static UserState fromString(String s) {
		String achievedStrs[] = s.split(";");
		UserState result = new UserState();
		for (String achievedStr: achievedStrs) {
			// A little bit defensive here: we reprocess them to
			// filter out ambiguous levels.
			result.addLevel(Level.fromString(achievedStr));
		}
		return result;
	}
	
	public void save(Context context) {
  	try {
  		DataOutputStream dos = new DataOutputStream(
  				context.openFileOutput(STATE_FILE_NAME, Context.MODE_PRIVATE));
  		dos.writeUTF(USER_DEFAULT);
  		dos.writeUTF(this.toString());
  		dos.close();
  	}
  	catch (IOException e) {
  		Log.e("UserState", "Error writing state: " + e.toString());
  	}
	}
	
	public static UserState load(Context context) {
    try {
    	DataInputStream dis = new DataInputStream(
    			context.openFileInput(STATE_FILE_NAME));
    	dis.readUTF();
    	UserState userState = UserState.fromString(dis.readUTF());
    	dis.close();
    	return userState;
    }
    catch (IOException e) {
    	Log.e("UserState", "Error reading state: " + e.toString());
    	return new UserState();
    }
	}

  private static final String USER_DEFAULT = "default";
  private static final String STATE_FILE_NAME = "guitar_ear.state";

	private Vector<Level> _achievedLevels;
}
